/*
 * Copyright 2007 Michał Baliński
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package pl.balon.gwt.diagrams.client.connection;

import java.util.ArrayList;
import java.util.List;

import pl.balon.gwt.diagrams.client.connection.calculator.ConnectionDataCalculator;
import pl.balon.gwt.diagrams.client.connection.calculator.FullRectilinearTwoEndedCalculator;
import pl.balon.gwt.diagrams.client.connection.data.ConnectionData;
import pl.balon.gwt.diagrams.client.connection.data.Point;
import pl.balon.gwt.diagrams.client.connection.pointwidget.MoveablePointWidget;
import pl.balon.gwt.diagrams.client.connector.Connector;
import pl.balon.gwt.diagrams.client.connector.Direction;

import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.Widget;

/**
 * Rectilinear connection widget
 *
 * @author Michał Baliński (michal.balinski@gmail.com)
 * @author Alex Pinheiro das Graças(alex.pgracas@gmail.com)
 */
public class RectilinearTwoEndedConnection extends AbstractTwoEndedDraggableConnection{

	static final String type = "RECTILINEAR";

	@Override
	public String getType() {
		return type;
	}
	/**
	 * DOM elements representing connection in browser dom tree
	 */
	private final List<Element> elements = new ArrayList<Element>();

	/**
	 * Constructs connection
	 */
	public RectilinearTwoEndedConnection(Connector[] toConnect) {
		super(toConnect);
		setElement(DOM.createDiv());
		addStyleName("gwt-diagrams-connection");
	}


	/**
	 * Constructs connection
	 */
	public RectilinearTwoEndedConnection(Connector c1, Connector c2) {
		this(new Connector[]{c1, c2});
	}

	/**
	 * @see pl.balon.gwt.diagrams.client.connection.AbstractConnection#createCalculator()
	 */
	@Override
	protected ConnectionDataCalculator createCalculator() {
		return new FullRectilinearTwoEndedCalculator();
	}

	@Override
	public void update() {
		if(this.data == null) {
			super.update();
		}else {
			FullRectilinearTwoEndedCalculator calculator = (FullRectilinearTwoEndedCalculator) this.getCalculator();
			this.data = calculator.calculateConnectionData(this.connected, data,-1,0,0);
			this.update(data);
		}
		if(isAttached()) {
			movablePointPositions(this.data);
		}
	}
	/**
	 * @see pl.balon.gwt.diagrams.client.connection.AbstractConnection#update(pl.balon.gwt.diagrams.client.connection.data.ConnectionData)
	 */
	@Override
	public void update(ConnectionData data) {
		if( data.getPoints().size() <= 1 ){
			throw new IllegalArgumentException("Too few connection points");
		}

		prepareElements(data.getPoints().size()-1);

		for (int i = 0; i < elements.size(); i++) {
			Element div = elements.get(i);
			if(i>1 && i < elements.size()-1 && isShowMoveablePoints()) {
				MoveablePointWidget widgetPoint = movablePoints.get(i-2);
				widgetPoint.setControledIndexPoint(i);
			}
			Point start = data.getPoints().get(i);
			Point end = data.getPoints().get(i+1);

			String style = "gwt-diagrams-line";

			DOM.setElementAttribute(div, "style", "");
			DOM.setElementProperty(div, "className", style);

			DOM.setStyleAttribute(div, "height", "1"); // TODO from css (IE impl bug)
			DOM.setStyleAttribute(div, "width", "1"); // TODO from css (IE impl bug)

			if( start.left == end.left ) {
				style += " gwt-diagrams-line-vertical";
				DOM.setStyleAttribute(div, "height", Integer.toString( Math.abs(start.top - end.top) ));
			} else if( start.top == end.top ) {
				DOM.setStyleAttribute(div, "width", Integer.toString( Math.abs(start.left - end.left) ));
				style += " gwt-diagrams-line-horizontal";
			}


//			} else {
// We are silent, nobody notice that inconsistence ;)
//				throw new IllegalStateException("Illegal state for right angeled connection");
//			}

			DOM.setStyleAttribute(div, "left", Integer.toString( Math.min(start.left, end.left) ));
			DOM.setStyleAttribute(div, "top", Integer.toString( Math.min(start.top, end.top) ));

			DOM.setElementProperty(div, "className", style);

			// Endings
			if( i==0 && getEnding(0)!=null ) {
				if( start.left < end.left ) { // LEFT
					getEnding(0).update(
							Math.min(start.left, end.left),
							Math.min(start.top, end.top),
							Direction.LEFT.getAngle());
				} else if( start.left > end.left ) { // RIGHT
					getEnding(0).update(
							Math.max(start.left, end.left),
							Math.min(start.top, end.top),
							Direction.RIGHT.getAngle());
				} else if( start.top > end.top ) { // DOWN
					getEnding(0).update(
							Math.min(start.left, end.left),
							Math.max(start.top, end.top),
							Direction.DOWN.getAngle());
				} else if( start.top < end.top ) { // UP
					getEnding(0).update(
							Math.min(start.left, end.left),
							Math.min(start.top, end.top),
							Direction.UP.getAngle());
				}
			} else if( i+1 == elements.size() && getEnding(1)!=null ){
				if( start.left > end.left ) { // LEFT
					getEnding(1).update(
							Math.min(start.left, end.left),
							Math.min(start.top, end.top),
							Direction.LEFT.getAngle());
				} else if( start.left < end.left ) { // RIGHT
					getEnding(1).update(
							Math.max(start.left, end.left),
							Math.min(start.top, end.top),
							Direction.RIGHT.getAngle());
				} else if( start.top < end.top ) { // DOWN
					getEnding(1).update(
							Math.min(start.left, end.left),
							Math.max(start.top, end.top),
							Direction.DOWN.getAngle());
				} else if( start.top > end.top ) { // UP
					getEnding(1).update(
							Math.min(start.left, end.left),
							Math.min(start.top, end.top),
							Direction.UP.getAngle());
				}
			}
		}

	}

	/**
	 * Recalculates elements collection. Creates new or remove some if
	 * necessary.
	 */
	private void prepareElements(int count) {
		Panel panel = (Panel) this.getParent();
		// New lines if too few
		for (int i = elements.size(); i < count; i++) {
			Element div = DOM.createDiv();
			elements.add(div);
			DOM.appendChild(getElement(), div);
			if(elements.size()>3 && isShowMoveablePoints()) {
				MoveablePointWidget w = createMovableWidget();
				panel.add((Widget)w);
				movablePoints.add(w);
			}
	        DOM.setInnerHTML(div, "&nbsp;");
		}

		// Remove some lines if too many
		while (elements.size() > count) {
			Element div = elements.remove(0);
			DOM.removeChild(getElement(), div);
			if(elements.size()>3 && isShowMoveablePoints()) {
				MoveablePointWidget w = movablePoints.remove(0);
				((Widget) w).removeFromParent();
				//fireEvent(new MovePointWidgetAction(w,Action.DELETE));
			}
		}

	}


	@Override
	public Point getCenter() {
		Point p1 = this.data.getPoints().get(Math.round(this.data.getPoints().size()/2));
		Point p2 = this.data.getPoints().get(Math.round(this.data.getPoints().size()/2+1));
		return new Point((p1.left+p2.left)/2,(p1.top+p2.top)/2);
	}

}
